package com.rt.tmpt.utils.rsa;


import javax.crypto.Cipher;
import java.io.*;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;

/**
 * RSA 工具类。提供加密，解密，生成密钥对等方法。
 * 需要到http://www.bouncycastle.org下载bcprov-jdk14-123.jar。
 * 
 */
public class RSAUtil {
	
	private static String RSAKeyStore = "C:/RSAKeyPair.key";
	

	/**
	 * * 生成密钥对  指定路径
	 * @return KeyPair *
	 * @throws EncryptException
	 */
	public static KeyPair generateKeyPair(String RSAKeyStore) throws Exception {
		try {
			KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA",
					new org.bouncycastle.jce.provider.BouncyCastleProvider());
			final int KEY_SIZE = 1024;// 没什么好说的了，这个值关系到块加密的大小，可以更改，但是不要太大，否则效率会低
			keyPairGen.initialize(KEY_SIZE, new SecureRandom());
			KeyPair keyPair = keyPairGen.generateKeyPair();
			saveKeyPair(keyPair,RSAKeyStore);
			return keyPair;
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		}
	}

	/**
	 * * 取密钥对公钥生成文件  指定路径
	 * @return KeyPair *
	 * @throws EncryptException
	 */
	public static PublicKey generateKeyPublic(KeyPair keyPair,String RSAKeyStorePublic) throws Exception {
		try {
			PublicKey publicKey = keyPair.getPublic();
			FileOutputStream fos = new FileOutputStream(RSAKeyStorePublic);
			ObjectOutputStream oos = new ObjectOutputStream(fos);
			// 生成密钥
			oos.writeObject(publicKey);
			oos.close();
			fos.close();
			
			return publicKey;
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		}
	}
	
	
	/**
	 * * 生成密钥对 *
	 * @return KeyPair *
	 * @throws EncryptException
	 */
	public static KeyPair generateKeyPair() throws Exception {
		try {
			KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA",
					new org.bouncycastle.jce.provider.BouncyCastleProvider());
			final int KEY_SIZE = 1024;// 没什么好说的了，这个值关系到块加密的大小，可以更改，但是不要太大，否则效率会低
			keyPairGen.initialize(KEY_SIZE, new SecureRandom());
			KeyPair keyPair = keyPairGen.generateKeyPair();
			//System.out.println(keyPair.getPrivate());
			//System.out.println(keyPair.getPublic());
			saveKeyPair(keyPair,null);
			return keyPair;
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		}
	}

	public static KeyPair getKeyPair(String keyPairLocation) throws Exception {
		FileInputStream fis = new FileInputStream(keyPairLocation);
		ObjectInputStream oos = new ObjectInputStream(fis);
		KeyPair kp = (KeyPair) oos.readObject();
		oos.close();
		fis.close();
		return kp;
	}

	public static KeyPair getKeyPairClasspath(String filename) throws Exception {
		ObjectInputStream oos = new ObjectInputStream(RSAUtil.class.getResourceAsStream("/"+filename));
		KeyPair kp = (KeyPair) oos.readObject();
		oos.close();
		return kp;
	}
	
	public static void saveKeyPair(KeyPair kp, String RSAKeyStore) throws Exception {
		FileOutputStream fos;
		if(RSAKeyStore==null){
			fos = new FileOutputStream(RSAUtil.RSAKeyStore);
		}else{
			fos = new FileOutputStream(RSAKeyStore);
		}
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		// 生成密钥
		oos.writeObject(kp);
		oos.close();
		fos.close();
	}

	/**
	 * * 生成公钥 *
	 * 
	 * @param modulus *
	 * @param publicExponent *
	 * @return RSAPublicKey *
	 * @throws Exception
	 */
	public static RSAPublicKey generateRSAPublicKey(byte[] modulus,
			byte[] publicExponent) throws Exception {
		KeyFactory keyFac = null;
		try {
			keyFac = KeyFactory.getInstance("RSA",
					new org.bouncycastle.jce.provider.BouncyCastleProvider());
		} catch (NoSuchAlgorithmException ex) {
			throw new Exception(ex.getMessage());
		}

		RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(new BigInteger(
				modulus), new BigInteger(publicExponent));
		try {
			return (RSAPublicKey) keyFac.generatePublic(pubKeySpec);
		} catch (InvalidKeySpecException ex) {
			throw new Exception(ex.getMessage());
		}
	}

	/**
	 * * 生成私钥 *
	 * 
	 * @param modulus *
	 * @param privateExponent *
	 * @return RSAPrivateKey *
	 * @throws Exception
	 */
	public static RSAPrivateKey generateRSAPrivateKey(byte[] modulus,
			byte[] privateExponent) throws Exception {
		KeyFactory keyFac = null;
		try {
			keyFac = KeyFactory.getInstance("RSA",
					new org.bouncycastle.jce.provider.BouncyCastleProvider());
		} catch (NoSuchAlgorithmException ex) {
			throw new Exception(ex.getMessage());
		}

		RSAPrivateKeySpec priKeySpec = new RSAPrivateKeySpec(new BigInteger(
				modulus), new BigInteger(privateExponent));
		try {
			return (RSAPrivateKey) keyFac.generatePrivate(priKeySpec);
		} catch (InvalidKeySpecException ex) {
			throw new Exception(ex.getMessage());
		}
	}

	/**
	 * * 加密 *
	 * 
	 * @param key
	 *            加密的密钥 *
	 * @param data
	 *            待加密的明文数据 *
	 * @return 加密后的数据 *
	 * @throws Exception
	 */
	public static byte[] encrypt(PublicKey pk, byte[] data) throws Exception {
		try {
			Cipher cipher = Cipher.getInstance("RSA",
					new org.bouncycastle.jce.provider.BouncyCastleProvider());
			cipher.init(Cipher.ENCRYPT_MODE, pk);
			int blockSize = cipher.getBlockSize();// 获得加密块大小，如：加密前数据为128个byte，而key_size=1024
			// 加密块大小为127
			// byte,加密后为128个byte;因此共有2个加密块，第一个127
			// byte第二个为1个byte
			int outputSize = cipher.getOutputSize(data.length);// 获得加密块加密后块大小
			int leavedSize = data.length % blockSize;
			int blocksSize = leavedSize != 0 ? data.length / blockSize + 1
					: data.length / blockSize;
			byte[] raw = new byte[outputSize * blocksSize];
			int i = 0;
			while (data.length - i * blockSize > 0) {
				if (data.length - i * blockSize > blockSize)
					cipher.doFinal(data, i * blockSize, blockSize, raw, i
							* outputSize);
				else
					cipher.doFinal(data, i * blockSize, data.length - i
							* blockSize, raw, i * outputSize);
				// 这里面doUpdate方法不可用，查看源代码后发现每次doUpdate后并没有什么实际动作除了把byte[]放到
				// ByteArrayOutputStream中，而最后doFinal的时候才将所有的byte[]进行加密，可是到了此时加密块大小很可能已经超出了
				// OutputSize所以只好用dofinal方法。

				i++;
			}
			return raw;
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		}
	}

	/**
	 * * 解密 *
	 * 
	 * @param key
	 *            解密的密钥 *
	 * @param raw
	 *            已经加密的数据 *
	 * @return 解密后的明文 *
	 * @throws Exception
	 */
	public static byte[] decrypt(PrivateKey pk, byte[] raw) throws Exception {
		try {
			Cipher cipher = Cipher.getInstance("RSA",
					new org.bouncycastle.jce.provider.BouncyCastleProvider());
			cipher.init(cipher.DECRYPT_MODE, pk);
			int blockSize = cipher.getBlockSize();
			ByteArrayOutputStream bout = new ByteArrayOutputStream(64);
			int j = 0;

			while (raw.length - j * blockSize > 0) {
				bout.write(cipher.doFinal(raw, j * blockSize, blockSize));
				j++;
			}
			return bout.toByteArray();
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		}
	}

	/**
	 * 私钥加密
	 * @param pk
	 * @param data
	 * @return
	 * @throws Exception
	 */
	public static byte[] encrypt(PrivateKey pk, byte[] data) throws Exception {
		try {
			Cipher cipher = Cipher.getInstance("RSA",
					new org.bouncycastle.jce.provider.BouncyCastleProvider());
			cipher.init(Cipher.ENCRYPT_MODE, pk);
			int blockSize = cipher.getBlockSize();// 获得加密块大小，如：加密前数据为128个byte，而key_size=1024
			// 加密块大小为127
			// byte,加密后为128个byte;因此共有2个加密块，第一个127
			// byte第二个为1个byte
			int outputSize = cipher.getOutputSize(data.length);// 获得加密块加密后块大小
			int leavedSize = data.length % blockSize;
			int blocksSize = leavedSize != 0 ? data.length / blockSize + 1
					: data.length / blockSize;
			byte[] raw = new byte[outputSize * blocksSize];
			int i = 0;
			while (data.length - i * blockSize > 0) {
				if (data.length - i * blockSize > blockSize)
					cipher.doFinal(data, i * blockSize, blockSize, raw, i
							* outputSize);
				else
					cipher.doFinal(data, i * blockSize, data.length - i
							* blockSize, raw, i * outputSize);
				// 这里面doUpdate方法不可用，查看源代码后发现每次doUpdate后并没有什么实际动作除了把byte[]放到
				// ByteArrayOutputStream中，而最后doFinal的时候才将所有的byte[]进行加密，可是到了此时加密块大小很可能已经超出了
				// OutputSize所以只好用dofinal方法。

				i++;
			}
			return raw;
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		}
	}

	
	/**
	 * * *
	 * 
	 * @param args *
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception {
		/*
		RSAPublicKey rsap = (RSAPublicKey) RSAUtil.generateKeyPair().getPublic();
		String test = "421550";
	    String keyPairLocation = "D:/cert/payment/RSAKeyPair.key";
		byte[] en_test = encrypt(getKeyPair(keyPairLocation).getPublic(), test.getBytes());
		String en_test_str = RSAUtil.byte2hex(en_test);
		System.out.println(en_test_str);
		byte[] de_test = decrypt(getKeyPair(keyPairLocation).getPrivate(), RSAUtil.hex2byte(en_test_str.getBytes()));
		System.out.println(new String(de_test));
		*/
		
		//RSAUtil.generateKeyPair();

		String Name= "BC";
		if (Security.getProvider(Name) == null)
		{
			System.out.println("not installed");
		}
		else
		{
			System.out.println("installed");
		}
	}
	
	private static String byte2hex(byte[] b){
		String hs = "";                   
		String stmp = "";                   
		for(int n = 0;n < b.length;n++)                  
		{                          
			stmp = (Integer.toHexString(b[n] & 0XFF));
			if(stmp.length() == 1)                          
			{                                  
				hs = hs + "0" + stmp;                          
			}                          
			else                          
			{                                  
				hs = hs + stmp;                          
			}                  
		}                  
		return hs.toUpperCase(); 
	}
	public static byte[] hex2byte(byte[] b){
		byte[] b2 = new byte[b.length / 2];                  
		for(int n = 0;n < b.length;n += 2)                  
		{                          
			String item = new String(b, n, 2);                          
			b2[n / 2] = (byte)Integer.parseInt(item, 16);                 
		}                  
		return b2;  
	}
	

	/**
	 * 从文件流中取公钥
	 * @param InputStream
	 * @return
	 * @throws Exception
	 */
	public static PublicKey getKeyPairPublic(InputStream keyStream) throws Exception {
		ObjectInputStream oos = new ObjectInputStream(keyStream);
		PublicKey kp = (PublicKey) oos.readObject();
		oos.close();
		return kp;
	}
	/**
	 * 从文件流中取私钥
	 * @param InputStream
	 * @return
	 * @throws Exception
	 */
	public static PrivateKey getKeyPairPrivate(InputStream keyStream) throws Exception {
		ObjectInputStream oos = new ObjectInputStream(keyStream);
		KeyPair kpair = (KeyPair) oos.readObject();
		PrivateKey pk = kpair.getPrivate();
		oos.close();
		return pk;
	}


	/**
	 * 公钥解密
	 * @param pk
	 * @param raw
	 * @return
	 * @throws Exception
	 */
	public static byte[] decrypt(PublicKey pk, byte[] raw) throws Exception {
		try {
			Cipher cipher = Cipher.getInstance("RSA",
					new org.bouncycastle.jce.provider.BouncyCastleProvider());
			cipher.init(cipher.DECRYPT_MODE, pk);
			int blockSize = cipher.getBlockSize();
			ByteArrayOutputStream bout = new ByteArrayOutputStream(64);
			int j = 0;

			while (raw.length - j * blockSize > 0) {
				bout.write(cipher.doFinal(raw, j * blockSize, blockSize));
				j++;
			}
			return bout.toByteArray();
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		}
	}
}
